package com.jch.dm2_guarded_suspension.demo_code;

import com.jch.dm2_guarded_suspension.reusable_code.Blocker;
import com.jch.dm2_guarded_suspension.reusable_code.ConditionVarBlocker;
import com.jch.dm2_guarded_suspension.reusable_code.GuardedAction;
import com.jch.dm2_guarded_suspension.reusable_code.Predicate;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;

// 嵌套监视器锁死示例代码
public class NestedMonitorLockoutExample {

    public static void main(String[] args){
        final Helper helper = new Helper();
        System.out.println("Before calling guardedMethod.");
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                String result;
                result = helper.xGuardedMethod("text");
                System.out.println(result);
            }
        });
        t.start();
        final Timer timer = new Timer();
        // 延迟50ms调用helper.stateChanged方法
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                helper.xStateChanged();
                timer.cancel();
            }
        },50,10);

    }

    private static class Helper {

        private volatile boolean isStateOK = false;

        private final Predicate stateBeOK = new Predicate() {
            @Override
            public boolean evaluate() {
                return isStateOK;
            }
        };

        private final Blocker blocker = new ConditionVarBlocker();

        public synchronized String xGuardedMethod(final String msg) {
            GuardedAction<String> ga = new GuardedAction<String>(stateBeOK) {
                @Override
                public String call() throws Exception {
                    return msg + "->received.";
                }
            };
            String result = null;
            try {
                result = blocker.callWithGuard(ga);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }

        public synchronized void xStateChanged() {
            try {
                blocker.signalAfter(new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                        isStateOK = true;
                        System.out.println("state ok.");
                        return Boolean.TRUE;
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 嵌套监视器锁死原因：
    /*
     * xGuardedMethod方法最终会调用到java.util.concurrent.locks.Condition实例的await方法。
     * Condition实例的await方法在其被调用之后返回之前会释放与其所属的Condition实例所关联的锁。但
     * xGuardedMethod方法本身所获得的锁（即guardedMethod方法所属的NestedMonitorLockoutExample实例）并没有释放。
     * 而Condition实例的await方法要返回需要其他线程调用Condition实例的signal/signalAll方法。但调用xStateChanged方法
     * 所需获得的锁此时还是被xGuardedMethod方法的执行线程所保留。这意味着其他线程无法通过调用xStateChanged方法使得xGuardedMethod方法
     * 所调用的Condition实例的await方法返回，而这点又导致xStateChanged方法调用时所需的锁无法被获得，就形成了锁死。
     */
}
